Stacked bar chart option for generic assay tracks#5539
Merged
Conversation
✅ Deploy Preview for cbioportalfrontend ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
772bf8d to
da428e5
Compare
alisman
reviewed
May 7, 2026
| })), | ||
| })); | ||
|
|
||
| // Fetch each profile's batches in parallel, then concat per profile so |
da428e5 to
32ba8aa
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Adds generic-assay stacked bar chart rendering to the results-view Oncoprint, including URL-driven chart modes, menu controls, sorting options, shared palettes/legends, and supporting oncoprintjs rendering fixes.
Changes:
- Adds heatmap, bar, stacked composition, and stacked absolute chart-type flows for generic assay tracks.
- Extends oncoprintjs stacked-bar rulesets, track menus, move controls, WebGL clipping, and async track-position handling.
- Adds robustness fixes for generic-assay data fetching/cache errors and targeted unit tests for sorting/prefix trimming.
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
src/shared/lib/LazyMobXCache.ts |
Passes an Error object when cached fetches fail. |
src/shared/lib/GenericAssayUtils/GenericAssayCommonUtils.ts |
Batches generic-assay data POSTs by stable ID chunks. |
src/shared/components/oncoprint/TooltipUtils.ts |
Adds stacked-bar tooltip formatting and filters null tooltip data. |
src/shared/components/oncoprint/SortUtils.ts |
Adds stacked-bar sort comparators. |
src/shared/components/oncoprint/SortUtils.spec.ts |
Covers stacked-bar sort comparator behavior. |
src/shared/components/oncoprint/ResultsViewOncoprintUtils.tsx |
Adds generic-assay chart-type entries to track-group menus. |
src/shared/components/oncoprint/ResultsViewOncoprint.tsx |
Wires stacked/bar generic-assay tracks, URL state, and profile reordering. |
src/shared/components/oncoprint/OncoprintUtils.ts |
Builds generic-assay heatmap/bar/stacked tracks, palettes, labels, menus, and aggregation. |
src/shared/components/oncoprint/OncoprintUtils.spec.ts |
Tests shared-prefix trimming. |
src/shared/components/oncoprint/Oncoprint.tsx |
Extends track spec types for bar and stacked-bar variants. |
src/shared/components/oncoprint/DeltaUtils.ts |
Transitions generic-assay bar/stacked tracks and custom options. |
src/shared/cache/GenericAssayMolecularDataCache.ts |
Hardens augmentation against null/non-matching generic-assay results. |
src/pages/studyView/addChartButton/genericAssaySelection/GenericAssaySelection.tsx |
Adds select-all and chart-type selection UI for generic assays. |
src/pages/resultsView/ResultsViewURLWrapper.ts |
Registers new generic-assay chart-mode URL parameters. |
src/pages/resultsView/oncoprint/TracksMenu.tsx |
Enables select-all and chart-type selector for generic assay tracks. |
src/AppStore.ts |
Guards global error handling against undefined errors. |
packages/oncoprintjs/src/js/oncoprintwebglcellview.ts |
Widens WebGL near plane for many stacked shapes. |
packages/oncoprintjs/src/js/oncoprinttrackoptionsview.ts |
Adds submenu, touch handling, and custom move up/down support. |
packages/oncoprintjs/src/js/oncoprintruleset.ts |
Adds absolute scaling support to stacked-bar rulesets. |
packages/oncoprintjs/src/js/oncoprintmodel.ts |
Adds custom track options/move state and fixes track-position/cluster-sort issues. |
.gitignore |
Ignores common-dist/. |
Comment on lines
+2281
to
+2287
| if (!anyCatHasAnySample) { | ||
| datum.na = true; | ||
| datum.attr_val_counts = {}; | ||
| } else { | ||
| datum.attr_val_counts = filled; | ||
| datum.attr_val = filled; | ||
| } |
Comment on lines
+1332
to
+1347
| // If the profile has no entities left, drop it from both stacked sets. | ||
| if (entities.length === 0) { | ||
| if (this.genericAssayStackedProfiles[molecularProfileId]) { | ||
| updates.generic_assay_stacked_profiles = this.serializeStackedProfiles( | ||
| _.omit(this.genericAssayStackedProfiles, molecularProfileId) | ||
| ); | ||
| } | ||
| if (this.genericAssayStackedAbsoluteProfiles[molecularProfileId]) { | ||
| updates.generic_assay_stacked_absolute_profiles = this.serializeStackedProfiles( | ||
| _.omit( | ||
| this.genericAssayStackedAbsoluteProfiles, | ||
| molecularProfileId | ||
| ) | ||
| ); | ||
| } | ||
| } |
Comment on lines
+282
to
+285
| export interface IStackedBarTrackSpec extends CategoricalTrackSpecBase { | ||
| stackedBar: true; | ||
| // Order of categories in each stack (bottom-to-top in rendering terms). | ||
| stackedBarCategories: string[]; |
Comment on lines
+2066
to
+2069
| const genericAssayProfileOrder = _.orderBy( | ||
| Object.values(molecularProfileIdToAdditionalTracks), | ||
| g => g.trackGroupIndex | ||
| ).map(g => g.molecularProfileId); |
Comment on lines
+1829
to
+1835
| if (prevSortBy !== nextStacked.stackedBarSortByCategory) { | ||
| // Category order changed (sort-by moves to the bottom of the | ||
| // stack), so rebuild the rule set to match the new visual order. | ||
| oncoprint.setRuleSet( | ||
| trackId, | ||
| getCategoricalTrackRuleSetParams(nextStacked) | ||
| ); |
Comment on lines
1681
to
1693
| if ( | ||
| rulesetTrackId === trackId && | ||
| (nextSpec as any).showAsBar !== (prevSpec as any).showAsBar | ||
| ) { | ||
| oncoprint.setRuleSet( | ||
| trackId, | ||
| getHeatmapTrackRuleSetParams( | ||
| nextSpec, | ||
| nextProps.isWhiteBackgroundForGlyphsEnabled | ||
| ) | ||
| ); | ||
| } | ||
| oncoprint.shareRuleSet(rulesetTrackId!, trackId); |
Adds a "stacked bar chart" visualization for generic assay tracks (composition + absolute modes), shared palette across paired profiles that describe the same cell types, a single legend when both paired tracks are visible, and fixes two pre-existing crashes in oncoprintjs (cluster-sort undefined length, addTrack race in label_view). Composition mode (each bar sums to 100%) and absolute mode (bars scaled to max total, bottom-anchored) reuse the existing STACKED_BAR rule set used by the Mutational Spectrum clinical track, so rendering matches the existing stacked-bar track. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The shader packs the shape-list index as gl_Position.z. The ortho projection's near plane was -5, mapping z_index=6..9 below NDC_z=-1 and getting frustum-clipped. This is only a visible bug for stacked tracks with 7+ categories — Mutational Spectrum (6 cats) stayed just inside the frustum, but the new generic-assay stacked-bar track used 10 cell-type categories and rendered at ~60% height (last 4 rects clipped). Widen near to -1000 so z_index has ~1000 shapes of headroom. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…p header - Add "Sort by <category>" items to the stacked-bar track menu. Selecting one sets the track as primary sorter with a descending-by-value comparator; "Sort by total (default)" clears it. Persists via URL param `generic_assay_stacked_sortby=<profileId>:<entityId>`. - Skip the big track-group section header (and its Cluster / Don't-cluster options) when a profile is displayed as a stacked-bar track — it's a single-row track that already carries its label, and cluster doesn't make sense for a single stacked track. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…adient Each entity gets its own row (as before) but the heatmap gradient is replaced by a bar whose height is proportional to the value, using the BAR rule set (same machinery as the clinical "number" track). Toggle from the per-row track menu: "Show as bar chart" / "Show as heatmap gradient". Persists via URL param \`generic_assay_bar_profiles\` (set of profile IDs, semicolon separated). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously fractions and counts rendered with a dark-blue-at-zero gradient (`[darkBlue, lightBlue, lightRed, darkRed]` when pivot in middle), which left a cold blue sliver at zero and made low values read as "absence of signal, blue" rather than "absence of signal, background". When leftBoundaryValue >= 0 we now skip the blue half of the scale entirely and use `[white, darkRed]` (or `[white, lightRed, darkRed]` if a pivot falls inside the data range). Negative-valued tracks (log2 ratios, etc.) still use the blue-to-red scheme. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the 11 flat "Sort by <cat>" items with a single "Sort by ▸" parent that reveals the category list on hover, unclutters the track menu. Adds optional \`children?: CustomTrackOption[]\` to CustomTrackOption; when present the option renders as a hover- expanding parent instead of a leaf. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Collapse all chart-type toggles (separate heatmap / separate bars / stacked composition / stacked absolute) under a single "Chart type ▸" hover submenu. Current selection marked with ✓. - Detect fraction-like profiles (max per-sample total <= 1.01 across all entities in the profile) and hide the "Stacked bar (absolute)" option for them — absolute mode on fractions produces identical 100% bars, so it's meaningless. - Unified setGenericAssayChartType action atomically toggles both stacked and bar URL params so transitions between the four view types don't leave inconsistent intermediate state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The stacked-bar track menu exposes a "Sort by >" submenu for picking a category, so the default "Sort a-Z / Sort Z-a / Don't sort" items were redundant. Added a sort_direction_menu_visible track param in oncoprintjs to gate rendering of those items while leaving programmatic sort intact. Also relabeled the default sort option to "Dominant category (default)" since the comparator sorts by dominant category + proportion, not by a total (which is always 1 for fraction-like data). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two related issues surfaced when using the generic-assay stacked-bar track menu on a phone: 1. Sort-by category never applied. Root cause: the MobxPromise that builds the stacked-bar track specs read oncoprint.genericAssayStackedSortBy after an `await`, so MobX stopped tracking it and the promise never re-invoked when URL state changed. Snapshot the observable before the first await to restore reactivity. 2. Menu items (Move up, Move down, Sort by submenu children, etc.) did not respond to taps on touch devices. Native click often doesn't fire on <li> elements on iOS Safari unless touch-action is explicit. Added touch-action: manipulation to remove the 300ms delay, and attached a touchend fallback alongside click with per-handler debounce. Also closes the outer dropdown when a submenu child is selected so the effect is visible. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a user picks a category to sort by, they're asking to compare that dimension across samples. Leaving the category mid-stack makes comparison hard because each bar's top edge drifts. Reorder the stack so the picked category renders at the bottom — every bar shares the same baseline, and the visual gradient reads cleanly. Delta detection also rebuilds the rule set on sort-by change so the new visual order takes effect without a full track re-add. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two small additions to the oncoprint UI: - GenericAssaySelection gains an optional selectAllThreshold prop. When set (TracksMenu passes 25), a "Select all" button appears next to the submit button — but only if the profile has few enough entities to make a one-click bulk-add reasonable. Larger profiles (hundreds of entities) keep the old behavior so users aren't one tap away from flooding the oncoprint. - Stacked-bar tracks in absolute mode get a "Total" entry in the Sort by submenu, above the per-category list. It orders samples by the sum of all category values descending. Intentionally hidden in composition mode, where every bar totals 1 and a total-sort is a no-op. Uses the sentinel "__total__" for stackedBarSortByCategory. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously, Move up/Move down in the track menu only reordered tracks within their containing group. That's fine when a group has siblings, but stacked-bar generic-assay tracks render as a single-track group without a header — so the user sees a track sitting next to genes and clinical rows and expects Move up/Down to reorder it past them, only to find the buttons were no-ops. Added a swapTrackGroups(i, j) pair on the model + Oncoprint. When the track has no siblings in its group, Move up/Down now swaps the whole (single-track) group with its neighbor. Within-group behavior is unchanged when siblings exist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously hidden because the new "Sort by" submenu looked like enough, but a-Z/Z-a actually flips the direction of whichever comparator is active (including the Sort-by-category one) — useful and distinct from picking the sort key. Removed the now-unused sort_direction_menu_visible flag introduced earlier in this branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sort-direction fix: the category/total comparators were written descending (largest first) with init_direction=1, which flipped the cbioportal a-Z = ascending convention. Sort a-Z ended up showing largest values first and Sort Z-a showed smallest — inverted from what the labels imply. Now the comparators sort ascending and the picked-category default uses direction=-1, so: - Sort a-Z → smallest value first (matches label) - Sort Z-a → largest value first (matches label) - Picked category default → largest first (what the UI wants initially) Menu reorder: Chart type ▸ moves to the bottom of the stacked-bar track menu with a separator above it. Everything else is sort-related; the separator groups them visually and puts the less-frequently-used rendering-mode toggle out of the way. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… its group" This reverts commit 67bfb79.
Move up/down were disabled whenever the track's group was flagged as clustered, even for groups with a single track — where clustering is a no-op and the grayed items just look broken. Require > 1 tracks in the group before considering the clustered flag a reason to disable moves. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…space Move up/down on a stacked-bar track is now a real reorder: the two generic-assay profiles' track-group indexes swap via a new swapGenericAssayProfileOrder action. The track spec keys include the current trackGroupIndex so the delta layer remove/re-adds tracks in the new groups, keeping React state and the oncoprintjs model in sync — no more desync on a later unstack. Items gray out at the limits: Move up is disabled when the profile is already at the top of the generic-assay section, Move down when it's at the bottom. Crossing into the hardcoded gene (1) or clinical (0) group is still out of scope; swap is limited to profiles that share the dynamic generic-assay index space, which keeps the change local. Plumbing: oncoprintjs gains on_move_up / on_move_down callback overrides and move_up_disabled / move_down_disabled flags on the track spec; ICategoricalTrackSpec gains the same (onMoveUp / onMoveDown / moveUpDisabled / moveDownDisabled); molecularProfileIdToTrackGroupIndex is now @observable so swaps propagate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The bulk-select button now sits to the left of the primary submit button and flips between "Select all" and "Clear all" based on whether anything is selected, so it's a one-tap toggle instead of two separate buttons. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Covers profiles like the 48-signature mutational-signature set on msk_impact_50k_2026 where a one-click "Select all" is genuinely useful. Still well below the 100-option search cap, so the button never appears when the dropdown is paginated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
onClickAddGenericAssays in ResultsViewOncoprint reached info[0].profileId with no prior length check, so submitting an empty track selection (now easier to trigger via "Clear all" + Add Track) threw. Bail out when info is empty. AppStore's global service-error handler assumed the error argument was always defined and would crash on `error.status` when a promise rejected with `undefined` — which then masked whatever failure triggered the reject. Guard the access so the handler survives undefined. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…abel prefix Scale fixes triggered by adding all 30 mutational signatures to the oncoprint on msk_impact_50k_2026 (~50k samples): - fetchGenericAssayData now chunks stableIds into batches of 5 per POST. A single request for all 30 signatures produced ~750 MB of row-per- datum JSON, which V8 couldn't parse and quietly returned null. Backend-side fix tracked separately: cBioPortal/cbioportal#12131. - augmentQueryResults skips null / non-iterable responses instead of throwing a TypeError that cascaded into a cache error state. - LazyMobXCache.tryTrigger now passes a real Error to the promise's error callback; previously called it with no arg, so every downstream handler saw `undefined` and could crash on `error.status`. UX fix for generic-assay split-mode labels: - Track labels from the same profile share the same long prefix ("mutational_signatures_contribution_v2_Signature1", etc.). Under the label-column truncation every row rendered as "mutational_sign...". Strip the common prefix per profile, cut at the last separator (_, -, ., :, whitespace) so a meaningful token stays. Labels become "Signature1", "Signature2", ..., "Signature30". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Users previously had to add tracks, then drill into each track's menu to switch Chart type ▸. Adds a radio row above the submit button (Separate rows heatmap / Separate rows bar chart / Stacked bar composition / Stacked bar absolute) that maps the selection into the URL's stacked / bar profile params via setGenericAssayChartType, so the tracks appear in the chosen form on the first render. Gated behind showChartTypeSelector so the study-view AddChart surface — which shares GenericAssaySelection — stays unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eader Tests: - SortUtils.spec: covers makeStackedBarTrackSortComparatorByCategory (ascending by picked category, NA-to-end, missing category treated as 0) and makeStackedBarTrackSortComparatorByTotal (sum ordering, NaN handling, NA-to-end). - OncoprintUtils.spec: covers commonPrefixLength — no-strip when shared prefix has no separator, cut at last separator within the common prefix, support for _ - . : and whitespace separators, prefix-of-another case. Exported the helper for this. UX: - Chart type options now appear in the generic-assay track-group header menu (Heatmap / Bar chart / Stacked bar (composition / absolute)), not just inside each per-row track's menu. Switching to a stacked mode swaps to the single-row render path; the header disappears as expected once the profile is stacked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restructure the Add Tracks dialog so the elements stack by purpose: - Row 1: entity search + Select all / Clear all - Row 2: "Chart type:" radios (flex-wrap row below the dropdown) - Row 3: full-width Add Track button The radios were cramped inline on the same row as the dropdown; dropping them to their own row gives them breathing room and keeps the primary Add Track CTA visually distinct as the final commit step. btn-block / inline width:100% wouldn't take effect here (something in the global CSS is pinning .btn width to content), so the button is flex: 1 inside a flex container which reliably stretches it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…inner ICategoricalTrackSpec is now a union of ISingleCategoricalTrackSpec (the classic single-value track) and IStackedBarTrackSpec (where the datum's attr_val is a category->number map and stackedBarCategories + stackedBarFills are required). Consumers narrow via `if (spec.stackedBar)` and drop the redundant `&& spec.stackedBarCategories` guards. Loading spinner on chart-type switch: - Include genericAssayStackedBarTracks in the isLoading check so the spinner shows while the stacked-bar promise is fetching. - setGenericAssayChartType now flips renderingComplete to false so the spinner also shows during the React re-render of the track set — swapping 30 per-entity tracks for 1 stacked track (or vice versa) takes long enough on large studies to look like a hang. The Oncoprint lifecycle calls onReleaseRendering once the new tracks are on screen, flipping the flag back. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A CPU-profile of a chart-type swap on msk_impact_50k_2026 (50k samples, 30 mutational signatures) showed 95s in exporter.trim — all reached via the core-js polyfills for parseInt and parseFloat. Two hot paths: 1. OncoprintModel.getRuleSets dedup'd numeric rule-set ids through a number → toString → arrayUnique → parseInt roundtrip. Replaced with Array.from(new Set(ids)) so numbers stay numbers. Cuts ~95s. 2. StackedBarRuleSet's per-shape height/y functions called parseFloat on every category's value for every sample, every render. Each call went through the polyfill's trim(). Replaced with the unary-plus coercion (`+d[...][...]`) — same result, no polyfill, ~5x faster on its own plus no trim overhead. Cuts ~93s (48s + 45s across height and y). Chart-type swap on that study goes from ~153s to ~61s end-to-end. More cleanup possible in the remaining WebGL vertex/shape loop (still the top post-fix hotspot at ~7.6s), but this is the big win. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same polyfill-avoidance fix as the stacked-bar shape functions: every
parseFloat call goes through core-js's polyfill which trims its input.
In the heatmap track build path we map every datum with
{...d, value: parseFloat(d.value)}, which for 30 signatures × 50k
samples on msk_impact_50k_2026 = 1.5M parseFloat calls per render.
Also two isFractionLikeByProfile loops that parseFloat every datum
value. All converted to +d.value — same behaviour, ~5s saved.
Also gitignore the common-dist/ build artifacts that rspack drops at
the repo root during dev; accidentally committing them dwarfs the
actual diff.
Note: the dominant remaining cost on that URL is seamless-immutable
deep-freezing the generic-assay data cache in LazyMobXCache.updateCache
(~30s in addPropertyTo + Immutable + the resulting GC). That's a
structural change to the shared cache implementation and not in
scope for this PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
32ba8aa to
f464045
Compare
Condense over-verbose comment blocks and drop change-history narration and comments that merely restate the code, keeping the non-obvious rationale (MobX await-tracking, sync track_tops, frustum near-plane, fraction detection, etc.). Comments only; no logic changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JEbP4a7tGv74r3RtNYt7Jw
Nothing in the build config, scripts, or templates produces or references a common-dist/ directory; the entry was unrelated to the stacked-bar work. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JEbP4a7tGv74r3RtNYt7Jw
- All-zero stacked rows render as N/A instead of NaN bar heights (builder marks zero-total rows NA; ruleset guards a zero denominator) - Clear generic_assay_bar_profiles when a profile's last entity is removed, so it doesn't reappear as a bar chart on re-add - Move up/down on stacked tracks stays within generic-assay profiles - Rebuild the stacked rule set when the entity set or absolute-mode max changes, not only on sort-by change - Rebuild the shared heatmap/bar rule set order-independently so all rows switch chart type on a heatmap<->bar toggle - Fix reversed top/bottom category-order doc comment Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JEbP4a7tGv74r3RtNYt7Jw
Match the prominent Add Track button so the two heatmap-add actions are styled consistently. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JEbP4a7tGv74r3RtNYt7Jw
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a "Show as stacked barchart" option for generic assay tracks. Composition mode (each bar = 100%) and absolute mode (bars scaled to max total, bottom-anchored) both route through the existing
STACKED_BARrule set used by the Mutational Spectrum clinical track, so rendering follows the existing pattern.oncoprintjscrashes surfaced while wiring this up:addTrackcluster-sort path pushingundefinedintogroup_arraysaddTracksrace wherelabel_viewreadtrack_topsbeforesort()completedTest study:
msk_spectrum_tme_2022(cell-type relative + absolute counts)Preview
Caveat: note that this surfaced an issue that this study actually uses FACS gating so we need some way to indicate that clearly. Will work on a follow up PR for that.
Closes cBioPortal/cbioportal#10747
🤖 Generated with Claude Code